#include "Tesseract.h"


Tesseract::Tesseract(vec4 _pos, Rotate4 _rot, float _sideLength):Shape(_pos, _rot){
	float half_side = _sideLength / 2.0;
	extents[0] = vec4(-half_side, -half_side, -half_side, -half_side);
	extents[1] = vec4(half_side, half_side, half_side, half_side);
	//set the side length
	//calculate the min and max bounds in object space
	//create an array of 16 vertices
	vec4 points[16];
	points[0] = vec4(extents[0].x, extents[0].y, extents[0].x, extents[0].w);
	points[1] = vec4(extents[0].x, extents[0].y, extents[0].x, extents[1].w);
	points[2] = vec4(extents[0].x, extents[0].y, extents[1].x, extents[0].w);
	points[3] = vec4(extents[0].x, extents[0].y, extents[1].x, extents[1].w);
	points[4] = vec4(extents[0].x, extents[1].y, extents[0].x, extents[0].w);
	points[5] = vec4(extents[0].x, extents[1].y, extents[0].x, extents[1].w);
	points[6] = vec4(extents[0].x, extents[1].y, extents[1].x, extents[0].w);
	points[7] = vec4(extents[0].x, extents[1].y, extents[1].x, extents[1].w);
	
	points[8] = vec4(extents[1].x, extents[0].y, extents[0].x, extents[0].w);
	points[9] = vec4(extents[1].x, extents[0].y, extents[0].x, extents[1].w);
	points[10] = vec4(extents[1].x, extents[0].y, extents[1].x, extents[0].w);
	points[11] = vec4(extents[1].x, extents[0].y, extents[1].x, extents[1].w);
	points[12] = vec4(extents[1].x, extents[1].y, extents[0].x, extents[0].w);
	points[13] = vec4(extents[1].x, extents[1].y, extents[0].x, extents[1].w);
	points[14] = vec4(extents[1].x, extents[1].y, extents[1].x, extents[0].w);
	points[15] = vec4(extents[1].x, extents[1].y, extents[1].x, extents[1].w);

	minBound = vec4(1e30f);
	maxBound = vec4(-1e30f);

	for (int i = 0; i < 16; i++){
		//transform the vertex into world space
		vec4 worldPt = invModelviewMat * points[i];	//rotate
		worldPt -= _pos;							//translate
		minBound.x = min(minBound.x, worldPt.x);
		minBound.y = min(minBound.y, worldPt.y);
		minBound.z = min(minBound.z, worldPt.z);
		minBound.w = min(minBound.w, worldPt.w);
		maxBound.x = max(maxBound.x, worldPt.x);
		maxBound.y = max(maxBound.y, worldPt.y);
		maxBound.z = max(maxBound.z, worldPt.z);
		maxBound.w = max(maxBound.w, worldPt.w);
	}
}


Tesseract::~Tesseract(){
}


bool Tesseract::intersect(Intersection &_int){
	Stats::instance()->primTest();	//increment the intersection test count
	//get the ray
	Ray _theRay = *(_int.ray());

	//transform ray into object space
	Ray t_ray = _theRay;
	t_ray.transform(modelviewMat, pos);
	vec4 rayOrig = t_ray.origin();
	vec4 rayDir = t_ray.direction();
	vec4 invDir = t_ray.invDirection();

	float tmin, tmax, tymin, tymax, tzmin, tzmax, twmin, twmax;
	//perform the intersection test
	tmin = (extents[t_ray.sign[0]].x - rayOrig.x) * invDir.x;
	tmax = (extents[1 - t_ray.sign[0]].x - rayOrig.x) * invDir.x;
	tymin = (extents[t_ray.sign[1]].y - rayOrig.y) * invDir.y;
	tymax = (extents[1 - t_ray.sign[1]].y - rayOrig.y) * invDir.y;
	if ((tmin > tymax) || (tymin > tmax))return false;
	if (tymin > tmin){
		tmin = tymin;
	}
	if (tymax < tmax){
		tmax = tymax;
	}
	tzmin = (extents[t_ray.sign[2]].z - rayOrig.z) * invDir.z;
	tzmax = (extents[1 - t_ray.sign[2]].z - rayOrig.z) * invDir.z;
	if ((tmin > tzmax) || (tzmin > tmax))return false;
	if (tzmin > tmin){
		tmin = tzmin;
	}
	if (tzmax < tmax){
		tmax = tzmax;
	}
	twmin = (extents[t_ray.sign[3]].w - rayOrig.w) * invDir.w;
	twmax = (extents[1 - t_ray.sign[3]].w - rayOrig.w) * invDir.w;
	if ((tmin > twmax) || (twmin > tmax))return false;
	if (twmin > tmin){
		tmin = twmin;
	}
	if (twmax < tmax){
		tmax = twmax;
	}

	if (tmax < 0)return false; //the tesseract is behind the ray
	if (tmax > t_ray.maxDist() || tmin < t_ray.minDist())return false;	//the intersection is too far away or too close

	float _dist = (tmin >= 0) ? tmin : tmax;

	if (_dist < _int.dist()){
		//set the new closest distance
		_int.dist(_dist);
		//set the new closest shape
		_int.shape(this);
		//set the surface normal at the intersection point
		vec4 _pt = _theRay.pointAtDepth(_dist);
		
		//IN BOX SPACE
		//get the spherical surface normal, isolate the largest component, and make that the new surface normal
		vec4 temp_pt = t_ray.pointAtDepth(_dist);
		vec4 temp_norm = abs(temp_pt);
		vec4 _norm;
		if (temp_norm.x > temp_norm.y && temp_norm.x > temp_norm.z && temp_norm.x > temp_norm.w){
			//x is greatest
			if (temp_pt.x > 0)
				_norm = vec4(1, 0, 0, 0);
			else
				_norm = vec4(-1, 0, 0, 0);
		}
		else if (temp_norm.y > temp_norm.x && temp_norm.y > temp_norm.z && temp_norm.y > temp_norm.w){
			//y is greatest
			if (temp_pt.y > 0)
				_norm = vec4(0, 1, 0, 0);
			else
				_norm = vec4(0, -1, 0, 0);
		}
		else if (temp_norm.z > temp_norm.y && temp_norm.z > temp_norm.x && temp_norm.z > temp_norm.w){
			//z is greatest
			if (temp_pt.z > 0)
				_norm = vec4(0, 0, 1, 0);
			else
				_norm = vec4(0, 0, -1, 0);
		}
		else{
			//w is greatest
			if (temp_pt.w > 0)
				_norm = vec4(0, 0, 0, 1);
			else
				_norm = vec4(0, 0, 0, -1);
		}
		//transform _norm back into world space
		_norm -= pos;
		_norm = invModelviewMat * _norm;

		_int.surfNorm(_norm);
	}

	_int.intersected(true);
	Stats::instance()->primInt();	//increment the intersection count
	return true;
}

vec4 Tesseract::getColor(Intersection &_int){
	//TODO
	return vec4(0);
}

vec4 Tesseract::getMaxBound(){
	return maxBound;
}
vec4 Tesseract::getMinBound(){
	return minBound;
}
vec4 Tesseract::getCenter(){
	return pos;
}